#include <stdio.h>
#include <string.h>

#include "SEGGER_RTT.h"

#include "FreeRTOS.h"
#include "task.h"

#include "base16.h"
#include "base64.h"
#include "encoding.h"

#include "drv_m5313.h"
#include "drv_bc26.h"

#include "misc.h"

static struct _ENV_M5313 {
    uint32_t imxi;
    uint32_t socket_id;
    uint32_t data_len;
    uint32_t data_pos;
}env_m5313;


void drv_m5313_cpin(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    BYTES res;
    MSG_AT msg_at;
    if (at_match(&bytes, "+s", &res)) {
        return;
    }
    if (str_cmp((const char *)res.pdata, res.len, CSTRING("READY"))) {
        NET_ERR("card not ready");
        return;
    }
    msg_at.info = AT_INFO_CARD_READY;
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("failed to pass msg");
        return;
    }
}

void drv_m5313_csq(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    uint32_t rssi;
    MSG_AT msg_at;

    if (at_match(&bytes, "+u,+u", &rssi, NULL)) {
        return;
    }
    msg_at.info = AT_INFO_CSQ;
    msg_at.value = rssi;
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("failed to pass msg %08x", msg_at.info);
        return;
    }
}

//+CREG:<n>,<stat>[,<lac>,<ci>]
void drv_m5313_creg(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    MSG_AT msg_at;
    uint32_t stat;
    msg_at.info = AT_INFO_CEREG;
    if (at_match(&bytes, "+u,+u", NULL, &stat)) {
        msg_at.value = 0;
    } else {
        msg_at.value = stat;
    }
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("failed to pass msg %08x", msg_at.info);
        return;
    }
}


void drv_m5313_send_ready(uint8_t *pdata, uint32_t len) {
    NET_DBG("send ready");
    MSG_AT msg_at;
    msg_at.result = AT_RESULT_OK;
    if (xQueueSend(env_net.qresult, &msg_at, 200) != pdPASS) {
        NET_ERR("fail to pass the result");
    }
}

void drv_m5313_send_ok(uint8_t *pdata, uint32_t len) {
    MSG_AT msg_at;
    msg_at.result = AT_RESULT_OK;
    if (xQueueSend(env_net.qresult, &msg_at, 200) != pdPASS) {
        NET_ERR("fail to pass the result");
    }
    msg_at.info = AT_INFO_SOCKET_SEND;
    msg_at.value = 0;
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("failed to pass msg %08x", msg_at.info);
        return;
    }
}


void drv_m5313_send_fail(uint8_t *pdata, uint32_t len) {
    MSG_AT msg_at;
    msg_at.result = AT_RESULT_OK;
    if (xQueueSend(env_net.qresult, &msg_at, 200) != pdPASS) {
        NET_ERR("fail to pass the result");
    }
    msg_at.info = AT_INFO_SOCKET_SEND;
    msg_at.value = 1;
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("failed to pass msg %08x", msg_at.info);
        return;
    }
}

void drv_m5313_connection_close(uint8_t *pdata, uint32_t len) {
    MSG_AT msg_at;
    msg_at.result = AT_RESULT_OK;
    if (xQueueSend(env_net.qresult, &msg_at, 200) != pdPASS) {
        NET_ERR("fail to pass the result");
    }
    msg_at.info = AT_INFO_SOCKET_SEND;
    msg_at.value = 2;
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("failed to pass msg %08x", msg_at.info);
        return;
    }
}

void drv_m5313_connect_fail(uint8_t *pdata, uint32_t len) {
    MSG_AT msg_at;
    msg_at.info = AT_INFO_SOCKET_CONNECT;
    msg_at.value = 1;
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("failed to pass msg %08x", msg_at.info);
        return;
    }
}

void drv_m5313_receive(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    int32_t socket_id, data_len;
    if (at_match(&bytes, ",+d,+d", &socket_id, &data_len) == 0) {
        NET_INF("socket_id=%d,data_len=%d", socket_id, data_len);
        if (BETWEEN10(0, socket_id, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
            env_m5313.socket_id = socket_id;
            env_m5313.data_len = data_len;
            env_m5313.data_pos = 0;
            return;
        } else {
            NET_ERR("invalid socket %d or length %d", socket_id, data_len);
        }
    } else {
        NET_ERR("failed to match");
    }
    env_m5313.socket_id = 0;
    env_m5313.data_len = 0;
    env_m5313.data_pos = 0;
}

static AT_LEXER_STATE at_lexer_state_receive(uint8_t data) {
    env_m5313.data_pos += 1;
    if (env_m5313.data_pos >= env_m5313.data_len) {
        if (env_m5313.data_len != 0) {
//          NET_INF("%c", data);
            app_fifo_put(&(env_net.pconns[env_m5313.socket_id].fifo), data);

            MSG_RECV msg_recv;
            msg_recv.type = MSG_RECV_SOCKET;
            msg_recv.value = env_m5313.socket_id;
            if (xQueueSend(env_net.qrecv, &msg_recv, 200) != pdPASS) {
                NET_ERR("failed to pass recv");
            }
        }
        return AT_LEXER_STATE_HEAD;
    } else {
//      NET_INF("%c", data);
        app_fifo_put(&(env_net.pconns[env_m5313.socket_id].fifo), data);
        return AT_LEXER_STATE_CUSTOM;
    }
}


//+MIPLEVENT:<ref>,<evtid>[,<ackid>]
void drv_m5313_miplevent(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    uint32_t ref, evt_id;
    MSG_AT msg_at;
    if (at_match(&bytes, "+u,+u", &ref, &evt_id)) {
        return;
    }
    if (ref != 0) {
        NET_ERR("invalid commnunication instance %d", ref);
        return;
    }
    if (evt_id == 2) {
        NET_INF("connect to bootstrap server successfully");
        return;
    } else if (evt_id == 6) {
        msg_at.info = AT_INFO_ONENET_EVENT;
        msg_at.value = 6;
    } else if (evt_id == 7) {
        msg_at.info = AT_INFO_ONENET_EVENT;
        msg_at.value = 7;
    }
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("fail to pass the info %08x", msg_at.info);
    }
}

//+MIPLOBSERVE:<ref>,<mid>,<flag>,<objid>,<insid>,<resid>
void drv_m5313_miplobserve(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    int32_t csi, inst_id;
    uint32_t obj_id;
    if (at_match(&bytes, "+d,+u,+u,+u,+d", &csi, NULL, NULL, &obj_id, &inst_id)) {
        return;
    }
    if (csi != 0) {
        NET_ERR("invalid communication instace %d", csi);
        return;
    }
    if (obj_id != CM_OBJ_ID) {
        NET_ERR("invalid obj_id:%d", obj_id);
        return;
    }
    if (inst_id == CM_INS_ID_UP) {
        NET_INF("observe the upload instance");
    } else if (inst_id == CM_INS_ID_DOWN) {
        NET_INF("observe the download instance");
    } else {
        NET_ERR("invalid instace %d", inst_id);
    }
}

void drv_m5313_mipldiscover(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    int32_t csi;
    uint32_t obj_id;
    MSG_AT msg_at;
    if (at_match(&bytes, "+d,+u,+u", &csi, NULL, &obj_id)) {
        return;
    }
    NET_INF("discovery");
    if (csi != 0) {
        NET_ERR("invalid communication instace %d", csi);
        return;
    }
    if (obj_id != CM_OBJ_ID) {
        NET_ERR("unkown object id %d", obj_id);
        return;
    }
    msg_at.info = AT_INFO_DISCOVER;
    //todo:discover mid
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("failed to pass the discover message");
    }
}

//+MIPLWRITE:<ref>,<mid>,<objid>,<insid>,<resid>,<type>,<len>,<value>,<flag>,<index>
//+MIPLWRITE:0,12823,3348,1,5750,2,8,4D544578,0
//+MIPLWRITE:0,40229,3348,1,5750,2,8,51554643,0
void drv_m5313_miplwrite(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    uint32_t mid;
    int32_t csi;
    int32_t obj_id, inst_id, res_id;
    MSG_RECV msg_recv;
    uint32_t data_type, data_len;
    if (at_match(&bytes, "+d,+u,+d,+d,+d,", &csi, &mid, &obj_id, &inst_id, &res_id)) {
        NET_ERR("failed to match write prefix");
        return;
    }
    if ((csi != 0) ||
        (obj_id != CM_OBJ_ID) ||
        (inst_id != CM_INS_ID_DOWN) ||
        (res_id != CM_RES_ID)) {
        pdata[len] = '\0';
        NET_ERR("invalid write:%s", pdata);
        return;
    }
    if (at_match(&bytes, "+u,+u,", &data_type, &data_len)) {
        return;
    }
    if (data_type != 2) {
        NET_INF("not support data type %d", data_type);
        return;
    }
    data_len = base64_base16_decode(bytes.pdata, data_len, bytes.pdata, bytes.len);
    if (data_len == 0) {
        NET_ERR("failed to decode by base64_base16");
        return;
    }
    msg_recv.type = MSG_RECV_WRITE;
    msg_recv.value = mid;
    if (xQueueSend(env_net.qrecv, &msg_recv, 200) != pdPASS) {
        NET_WAR("failed to pass the write msg");
    }
    app_fifo_write(&(env_net.pconns[0].fifo), bytes.pdata, &data_len);
    msg_recv.type = MSG_RECV_SOCKET;
    msg_recv.value = 0;
    if (xQueueSend(env_net.qrecv, &msg_recv, 200) != pdPASS) {
        NET_WAR("failed to pass the socket msg");
    }
}


int drv_m5313_any(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    int32_t socket_id;
    BYTES connect_result;
    MSG_AT msg_at;
    if (!str_all_number_pre((const char *)pdata, len)) {
        pdata[len] = '\0';
        if (env_m5313.imxi == 0) {
            if (len >= sizeof(env_net.pimei)) {
                NET_ERR("invalid imei:%s", pdata);
                return -1;
            }
            strcpy(env_net.pimei, (const char *)pdata);
//          NET_INF("imei:%s", pdata);
        } else {
            if (len >= sizeof(env_net.pimsi)) {
                NET_ERR("invalid imsi:%s", pdata);
                return -1;
            }
            strcpy(env_net.pimsi, (const char *)pdata);
//          NET_INF("imsi:%s", pdata);
        }
        return 0;
    } else if (!at_match(&bytes, "+u,+s", &socket_id, &connect_result)) { //<index>,<result>
        msg_at.info = AT_INFO_SOCKET_CONNECT;
        msg_at.value = -1;
        if (!str_cmp((const char *)connect_result.pdata, connect_result.len, CSTRING("CONNECT OK"))) {
            msg_at.value = 0;
        } else if (!str_cmp((const char *)connect_result.pdata, connect_result.len, CSTRING("CONNECT FAIL"))) {
            msg_at.value = 1;
        } else if (!str_cmp((const char *)connect_result.pdata, connect_result.len, CSTRING("ALREADY CONNECT"))) {
            msg_at.value = 2;
        } else if (!str_cmp((const char *)connect_result.pdata, connect_result.len, CSTRING("CLOSED"))) {
            //keep msg_at.value = -1
            NET_INF("socket %d was closed", socket_id);
        } else {
            msg_at.value = 3;
        }
        if (msg_at.value >= 0) {
            if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
                NET_ERR("failed to pass msg %08x", msg_at.info);
                return -1;
            }
        }
        return 0;
    } else {
        return -1;
    }
}


AT_HANDLER drv_m5313_at_handlers[] = {
    AT_HANDLER_GENERAL_DEF("OK", drv_net_result_ok),
    AT_HANDLER_GENERAL_DEF("ERROR", drv_net_result_error),
    AT_HANDLER_GENERAL_DEF("+CPIN:", drv_m5313_cpin),
    AT_HANDLER_GENERAL_DEF("+CSQ:", drv_m5313_csq),
    AT_HANDLER_GENERAL_DEF("+CEREG:", drv_m5313_creg),
    AT_HANDLER_SPECIAL_DEF("+RECEIVE", drv_m5313_receive, at_lexer_state_receive),
    AT_HANDLER_GENERAL_DEF("> ", drv_m5313_send_ready),
    AT_HANDLER_GENERAL_DEF("SEND OK", drv_m5313_send_ok),
    AT_HANDLER_GENERAL_DEF("SEND FAIL", drv_m5313_send_fail),
    AT_HANDLER_GENERAL_DEF("CONNECTION CLOSE", drv_m5313_connection_close),
    AT_HANDLER_GENERAL_DEF("CONNECT FAIL", drv_m5313_connect_fail),
    //onenet
    AT_HANDLER_GENERAL_DEF("+MIPLEVENT:", drv_m5313_miplevent),
    AT_HANDLER_GENERAL_DEF("+MIPLOBSERVE:", drv_m5313_miplobserve),
    AT_HANDLER_GENERAL_DEF("+MIPLDISCOVER:", drv_m5313_mipldiscover),
    AT_HANDLER_GENERAL_DEF("+MIPLWRITE:", drv_m5313_miplwrite),
    AT_HANDLER_ANY(drv_m5313_any)
};



NET_ERR_CODE drv_m5313_try_to_get_ip(void) {
    NET_ERR_CODE ret;
    uint32_t i;
    MSG_AT msg_at;
    //close echo
    ret = drv_net_write_bytes_raw(1000, 2, TSTRING(uint8_t * , "ATE0\r"));
    if (ret < 0) {
        NET_ERR("failed to close echo");
        return NET_ERR_UNKOWN;
    }

    //read imei
    env_m5313.imxi = 0;
    ret = drv_net_write_bytes_raw(1000, 2, TSTRING(uint8_t * , "AT+GSN=1\r"));
    if (ret < 0) {
        NET_ERR("failed to read imei");
        return NET_ERR_UNKOWN;
    }

    //read imsi
    env_m5313.imxi = 0xFFFFFFFF;
    ret = drv_net_write_bytes_raw(2000, 2, TSTRING(uint8_t * , "AT+CIMI\r"));
    if (ret < 0) {
        NET_ERR("no sim card");
        return NET_ERR_NO_CARD;
    }

    //check sim card
    xQueueReset(env_net.qinfo);
    ret = drv_net_write_bytes_raw(2000, 2, TSTRING(uint8_t * , "AT+CPIN?\r"));
    if (ret < 0) {
        NET_ERR("card is not ready");
        return NET_ERR_UNKOWN;
    }

    if (drv_net_msg_at_receive(AT_INFO_CARD_READY, 5, 1000, &msg_at, NULL) != NET_ERR_OK) {
        NET_ERR("card is not ready");
        return NET_ERR_UNKOWN;
    }

    //disable psm
    ret = drv_net_write_bytes_raw(1000, 2, TSTRING(uint8_t * , "AT+CPSMS=0,,,\"01000101\",\"00100001\"\r"));
    if (ret < 0) {
        NET_ERR("failed to disable psm");
        return ret;
    }

    //confiure dual mode
    ret = drv_net_write_bytes_raw(1000, 2, TSTRING(uint8_t * , "AT+CFGDUALMODE=1,0\r"));
    if (ret < 0) {
        NET_ERR("failed to configue mode");
        return ret;
    }


    drv_net_write_bytes_raw(1000, 2, TSTRING(uint8_t * , "AT+CFGRATPRIO?\r"));

    //2g priority
    ret = drv_net_write_bytes_raw(1000, 2, TSTRING(uint8_t * , "AT+CFGRATPRIO=2\r"));
    if (ret < 0) {
        NET_ERR("failed to set 2g priority");
        return ret;
    }

    drv_net_write_bytes_raw(1000, 2, TSTRING(uint8_t * , "AT+CFGRATPRIO?\r"));

    //reg?
    for (i = 60; i > 0; i--) {
        xQueueReset(env_net.qinfo);
        ret = drv_net_write_bytes_raw(1000, 2, TSTRING(uint8_t *, "AT+CEREG?\r"));
        if (ret < 0) {
            NET_ERR("module might be broken");
            return ret;
        }
        if (drv_net_msg_at_receive(AT_INFO_CEREG, 5, 1000, &msg_at, NULL) == NET_ERR_OK) {
            if ((msg_at.value == 1) || (msg_at.value == 5)) {
                if (msg_at.value == 5) {
                    NET_WAR("module is roaming");
                }
                break;
            }
            vTaskDelay(1000);
        }
        NET_INF("try to get ip%d...", i);
        if (i == 1) { //last loop
            NET_ERR("failed to register");
            drv_net_write_bytes_raw(1000, 2, TSTRING(uint8_t * , "AT+CFGRATPRIO=2\r"));
            return NET_ERR_UNKOWN;
        }
    }

    //check signal quality
    for (i = 20; i > 0; i--) {
        xQueueReset(env_net.qinfo);
        ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+CSQ\r"));
        if (ret < 0) {
            NET_ERR("module might be broken");
            return ret;
        }
        if (drv_net_msg_at_receive(AT_INFO_CSQ, 5, 1000, &msg_at, NULL) == NET_ERR_OK) {
            if (msg_at.value > 10 && msg_at.value != 99) {
                break;
            }
            vTaskDelay(1000);
        }
        if (i == 1) { //last loop
            NET_ERR("signal is not good");
            return NET_ERR_UNKOWN;
        }
    }

    //Activate PDP Context
    ret = drv_net_write_bytes_raw(1000, 2, TSTRING(uint8_t * , "AT+CGACT=1,1\r"));
    if (ret < 0) {
        NET_ERR("failed to activate context");
        return ret;
    }

    //enter the multi-connection mode
    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+CMMUX=1\r"));
    if (ret < 0) {
        NET_ERR("failed to enter multi-connection mode");
        return ret;
    }

    drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+CMPROMPT?\r"));


    NET_INF("registered successfully");
    return NET_ERR_OK;
}



static NET_ERR_CODE drv_m5313_socket_connect(int32_t socket,
                                             DRV_NET_SOCKET_TYPE service_type,
                                             char *pstr_ip,
                                             char *pstr_port,
                                             uint32_t fifo_size) { 
    NET_ERR_CODE ret;
    MSG_AT msg_at;
    char pbuf[64];
    int32_t len;
    if (!BETWEEN10(0, socket, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalid socket");
        return NET_ERR_UNKOWN;
    }
    if ((service_type != DRV_NET_SOCKET_TCP) && (service_type != DRV_NET_SOCKET_UDP)) {
        NET_ERR("invalid service type%d", service_type);
        return NET_ERR_UNKOWN;
    }
    if ((pstr_ip == NULL) || (pstr_port == NULL)) {
        NET_ERR("");
        return NET_ERR_UNKOWN;
    }

    //init fifo
    ret = drv_net_fifo_init(&(env_net.pconns[socket].fifo), fifo_size);
    if (ret < 0) {
        NET_ERR("unknown");
        return ret;
    }

    //AT+IPSTART=1,"TCP","183.230.40.150",36000
    if (service_type == DRV_NET_SOCKET_TCP) {
        len = snprintf(pbuf, sizeof(pbuf), "AT+IPSTART=%d,\"TCP\",\"%s\",%s\r", socket, pstr_ip, pstr_port);
    } else {
        len = snprintf(pbuf, sizeof(pbuf), "AT+IPSTART=%d,\"UDP\",\"%s\",%s\r", socket, pstr_ip, pstr_port);
    }

    if (len < 0) {
        NET_ERR("failed to format");
        return NET_ERR_UNKOWN;
    }

    xQueueReset(env_net.qinfo);
    ret = drv_net_write_bytes_raw(2000, 1, (uint8_t *)pbuf, len);
    if (ret < 0) {
        NET_ERR("faile to connect");
        return ret;
    }

    if (drv_net_msg_at_receive(AT_INFO_SOCKET_CONNECT, 40, 1000, &msg_at, "socket connect") == NET_ERR_OK) {
        if (msg_at.value == 0) { //connect ok
            NET_INF("connect successfully");
            return NET_ERR_OK;
        } else if (msg_at.value == 1) { //connect fail
            NET_ERR("failed to connect");
            return NET_ERR_UNKOWN;
        } else if (msg_at.value == 2) { //already connect
            NET_WAR("already connect");
        } else {
            NET_ERR("unkown socket connect result %d", msg_at.value);
            return NET_ERR_UNKOWN;
        }
    }
    NET_ERR("socket connection timed out");
    return NET_ERR_UNKOWN;
}

NET_ERR_CODE drv_m5313_socket_send(int32_t socket, uint8_t *pdata, uint32_t len) {
    uint8_t *pbuf;
    int32_t pbuf_len;
    NET_ERR_CODE ret;
    MSG_AT msg_at;
    if (pdata == NULL) {
        NET_ERR("null pointer");
        return NET_ERR_UNKOWN;
    }
    if (!BETWEEN10(0, socket, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalid socket %d", socket);
        return NET_ERR_UNKOWN;
    }
    pbuf = pvPortMalloc(len + 64);
    if (pbuf == NULL) {
        NET_ERR("failed to malloc send buffer");
        return NET_ERR_UNKOWN;
    }
    pbuf_len = snprintf((char *)pbuf, 64, "AT+IPSEND=%d,%d\r", socket, len);
    if (pbuf_len < 0) {
        NET_ERR("failed to format");
        vPortFree(pbuf);
        return NET_ERR_UNKOWN;
    }

    ret = drv_net_write_bytes_raw(1000, 1, pbuf, pbuf_len);
    if (ret < 0) {
        NET_ERR("transmission is not ready");
        vPortFree(pbuf);
        return ret;
    }

    memcpy(pbuf, pdata, len);
    pbuf[len] = 0x1A; //ctrl+z
    len += 1;

    ret = drv_net_write_bytes_raw(10000, 1, pbuf, len);

    vPortFree(pbuf);

    if (ret < 0) {
        NET_ERR("failed to send");
        return ret;
    }


    if (drv_net_msg_at_receive(AT_INFO_SOCKET_SEND, 10, 1000, &msg_at, NULL) == NET_ERR_OK) {
        if (msg_at.value == 0) { //send ok
            return NET_ERR_OK;
        } else if (msg_at.value == 1) { //send fail
            NET_ERR("failed to send");
            return NET_ERR_UNKOWN;
        } else if (msg_at.value == 2) { //already connect
            NET_WAR("socket %d was closed", socket);
            return NET_ERR_UNKOWN;
        } else {
            NET_ERR("unkown socket connect result %d", msg_at.value);
            return NET_ERR_UNKOWN;
        }
    }
    return NET_ERR_UNKOWN;
}

NET_ERR_CODE drv_m5313_socket_disconnect(int32_t socket) {
    char pbuf[32];
    int32_t len;
    NET_ERR_CODE ret;
    if (!BETWEEN10(0, socket, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalid socket:%d", socket);
        return NET_ERR_UNKOWN;
    }

    len = snprintf(pbuf, sizeof(pbuf), "AT+IPCLOSE=%d\r", socket);
    if (len <= 0) {
        NET_ERR("failed to format");
        return NET_ERR_UNKOWN;
    }

    ret = drv_net_write_bytes_raw(10000, 1, (uint8_t *)pbuf, len);
    if (ret < 0) {
        NET_ERR("failed to close the socket %d", socket);
        return ret;
    }
    return NET_ERR_OK;
}

NET_ERR_CODE drv_m5313_power_set(NET_POWER_CONTROL net_power) {
    if (net_power == NET_POWER_ON) {
        env_net.pat_handlers = drv_m5313_at_handlers;
        return drv_m5313_try_to_get_ip();
    } else if (net_power == NET_POWER_OFF) {
        drv_net_power_off();
        return NET_ERR_OK;
    } else if (net_power == NET_POWER_SLEEP) {
        return NET_ERR_UNKOWN;
    } else if (net_power == NET_POWER_WAKE) {
        return NET_ERR_UNKOWN;
    } else {
        NET_ERR("invalid power state");
        return NET_ERR_UNKOWN;
    }
}


NET_ERR_CODE drv_m5313_cm_connect(char *pstr_ip, char *pstr_port, uint32_t fifo_size) {
    char pbuf[64];
    int32_t len;
    MSG_AT msg_at;
    NET_ERR_CODE ret;
    //init fifo
    ret = drv_net_fifo_init(&(env_net.pconns[0].fifo), fifo_size);
    if (ret < 0) {
        NET_ERR("unknown");
        return ret;
    }

    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+MIPLCREATE=49,130031F10003F2002304001100000000000010123138332E3233302E34302E33393A35363833000131F300080000000000,0,49,0\r"));
    if (ret < 0) {
        NET_ERR("failed to create intance of communication");
        return ret;
    }
    //AT+MIPLADDOBJ=0,3348,2,"11",1,2
    len = snprintf(pbuf, sizeof(pbuf), "AT+MIPLADDOBJ=0,%d,2,\"11\",1,2\r", CM_OBJ_ID);
    if (len < 0) {
        NET_ERR("failed to format");
        return NET_ERR_UNKOWN;
    }
    ret = drv_net_write_bytes_raw(1000, 1, (uint8_t *)pbuf, len);
    if (ret < 0) {
        NET_ERR("failed to add object");
        return ret;
    }

    //AT+MIPLDISCOVERRSP=0,3348,1,4,"5750"
    len = snprintf(pbuf, sizeof(pbuf), "AT+MIPLDISCOVERRSP=0,%d,1,%d,\"%s\"\r", CM_OBJ_ID, sizeof(CM_RES_ID_STR) - 1, CM_RES_ID_STR);
    if (len < 0) {
        NET_ERR("failed to format");
        return NET_ERR_UNKOWN;
    }
    ret = drv_net_write_bytes_raw(1000, 1, (uint8_t *)pbuf, len);
    if (ret < 0) {
        NET_ERR("failed to add discovery");
        return ret;
    }

    xQueueReset(env_net.qinfo);
    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+MIPLOPEN=0,86400,60\r"));
    if (ret < 0) {
        NET_ERR("failed to open");
        return ret;
    }

    if (drv_net_msg_at_receive(AT_INFO_ONENET_EVENT, 40, 1000, &msg_at, "wait for registration") != NET_ERR_OK) {
        NET_ERR("registration timeout");
        return NET_ERR_UNKOWN;
    }

    if (msg_at.value == 6) {
        NET_INF("registered the platform successfully");
    } else if (msg_at.value == 7) {
        NET_ERR("failed to register the platform");
        return NET_ERR_UNKOWN;
    } else {
        NET_ERR("invalid value %d of inf %08x", msg_at.value, msg_at.info);
        return NET_ERR_UNKOWN;
    }

    if (drv_net_msg_at_receive(AT_INFO_DISCOVER, 40, 1000, &msg_at, "wait for discovery") != NET_ERR_OK) {
        NET_ERR("discovery timeout");
        return NET_ERR_UNKOWN;
    }

    NET_INF("discovered successfully");
    return NET_ERR_OK;
}

NET_ERR_CODE drv_m5313_cm_send(uint8_t *pdata, uint32_t len) {
    uint8_t *pbuf;
    int32_t buf_len;
    NET_ERR_CODE ret;

    pbuf = pvPortMalloc(BASE64_ENCODE_LEN(len) + 64);
    if (pbuf == NULL) {
        NET_ERR("failed to alloc");
        return NET_ERR_UNKOWN;
    }
    //AT+MIPLNOTIFY=0,0,3348,0,5750,1,4,"test",0,0
    buf_len = snprintf((char *)pbuf, 64, "AT+MIPLNOTIFY=0,0,%d,%d,%s,1,%d,\"", CM_OBJ_ID, CM_INS_ID_UP, CM_RES_ID_STR, BASE64_ENCODE_LEN(len));
    if (buf_len < 0) {
        NET_ERR("failed to format");
        vPortFree(pbuf);
        return NET_ERR_UNKOWN;
    }
    len = base64_encode(pdata, len, pbuf + buf_len, BASE64_ENCODE_LEN(len));
    if (len == 0) {
        NET_ERR("failed to encode by base64");
        vPortFree(pbuf);
        return NET_ERR_UNKOWN;
    }
    buf_len += len;
    memcpy(pbuf + buf_len, CSTRING("\",0,0\r"));
    buf_len += (sizeof("\",0,0\r") - 1);

    ret = drv_net_write_bytes_raw(3000, 1, pbuf, buf_len);
    vPortFree(pbuf); //useless
    if (ret < 0) {
        NET_ERR("failed to send data");
        return NET_ERR_UNKOWN;
    }
    return NET_ERR_OK;
}

NET_ERR_CODE drv_m5313_cm_disconnect(void) {
    char pbuf[64];
    int32_t len;
    NET_ERR_CODE ret;
    //AT+MIPLDELOBJ=0,3348
    len = snprintf(pbuf, sizeof(pbuf), "AT+MIPLDELOBJ=0,%d\r", CM_OBJ_ID);
    ret = drv_net_write_bytes_raw(10000, 1, (uint8_t *)pbuf, len);
    if (ret < 0) {
        NET_ERR("");
        return ret;
    }
    //AT+MIPLCLOSE=0
    ret = drv_net_write_bytes_raw(10000, 1, TSTRING(uint8_t *,"AT+MIPLCLOSE=0\r"));
    if (ret < 0) {
        NET_ERR("");
        return ret;
    }
    //AT+MIPLDELETE=0
    ret = drv_net_write_bytes_raw(10000, 1, TSTRING(uint8_t *,"AT+MIPLDELETE=0\r"));
    if (ret < 0) {
        NET_ERR("");
        return ret;
    }
    return NET_ERR_OK;
}

const DRV_NET_INTERFACE drv_m5313_interface = {
    .pf_power_set = drv_m5313_power_set,
    .pf_executor = NULL,
    //ct
    .pf_ct_connect = NULL,
    .pf_ct_send = NULL,
    .pf_ct_disconnect = NULL,
    //cm
    .pf_cm_connect = drv_m5313_cm_connect,
    .pf_cm_send = drv_m5313_cm_send,
    .pf_cm_disconnect = drv_m5313_cm_disconnect,
    //socket
    .pf_socket_connect = drv_m5313_socket_connect,
    .pf_socket_send = drv_m5313_socket_send,
    .pf_socket_disconnect = drv_m5313_socket_disconnect
};


